improve startup perf - #1030
Conversation
markt-asf
left a comment
There was a problem hiding this comment.
Given the first, simple change I reviewed missed an obvious simplification, I think a thorough review is required (which I don't have time for right now but I will try and work through this bit by bit as time allows).
It may be helpful if this PR was refactored to use one commit per issue as that would provide a natural breakdown of this large commit into more manageable chunks.
I also wonder if one PR per issue would be easier to manage / follow the review process as I suspect a PR of this size might become difficult to follow - particularly if it is reviewed/applied in stages and during that process the underlying code changes for other reasons.
| String[] names = findParameters(); | ||
| for (String s : names) { | ||
| mergedParams.put(s, findParameter(s)); | ||
| for (Map.Entry<String,String> parameter : parameters.entrySet()) { | ||
| mergedParams.put(parameter.getKey(), parameter.getValue()); |
There was a problem hiding this comment.
This could have used Map.putAll(Map).
|
i did it by design in one PR as many files only contains very small changes. |
# Conflicts: # java/org/apache/catalina/core/StandardContext.java
What
A set of internal, behavior-preserving optimizations that reduce web application
deployment/startup time. There are no changes to defaults, configuration,
public API, parsing output, or threading model — outputs are identical; only
redundant work and allocations are removed.
Changes
Annotation / JAR scanning (
ContextConfig)@HandlesTypestypes by name (class-file descriptor form forannotations, binary name for classes) so matching a scanned class is an O(1)
lookup instead of a linear scan of all handled types plus a per-annotation
string conversion.
META-INF/resources/during theannotation scan (which already iterates every entry), so
processResourceJARsno longer re-opens and re-scans each JAR.
web.xmlfreshness withFile.lastModified()forfile:URIs instead of opening aURLConnection.Collections.singletonfor the per-fragment annotation merge.Mapper (
Mapper/ batch registration)a single merge per mapping category instead of reallocating the array once per
mapping (previously O(n²)). Verified equivalent to the old sequential
insertMapbehaviour (sorted order, existing-wins dedup, wildcardnesting)over 300k randomized cases.
Web resources
JarWarResourceSet: build theMETA-INFbloom filter once when the entriesare read rather than rebuilding it on every resource lookup.
AbstractSingleArchiveResourceSet: pre-size the entry map from the JAR entrycount.
StandardRoot: hoist a loop invariant inlistResources;Collections.addAllinstead of
Arrays.asListwrapper.AbstractFileResourceSet/FileResource:file()already resolves thecanonical path to validate that a file is located under the resource set base,
then discards it, leaving
FileResource.getCanonicalPath()to resolve the samefile a second time. The body of
file()moves to a newvalidatedFile()thatreturns the file together with that path, and the two resource sets that create
a
FileResourcepass it on.file()is kept for its other callers.Misc internal
StandardJarScanner: hoist loop-invariantgetJarFileURL()/toURI()out of themanifest
Class-Pathloop;Collections.addAllfor the class-path URLs.Digester.updateAttributes: resolve the class loader once per call.FragmentJarScannerCallback: single map lookup for duplicate detection.LifecycleBase.fireLifecycleEvent: skip allocating the event when there are nolisteners.
AbstractProtocol.init: compute the (built/quoted) name once.MbeansDescriptorsIntrospectionSource:Setmembership test instead of alinear array scan.
Correctness
All changes preserve observable behaviour. The
Mapperchange was fuzz-checkedagainst the original sequential implementation (identical arrays and nesting).
TestContextConfigAnnotationis updated to build the new derived index (mirroringthe normal
processClassesflow).The canonical path reuse does not weaken the resource set validation: the checks
in
file()are unchanged, and the path is only handed to theFileResourcethatthose checks just validated. It is
nullwhengetAllowLinking()short-circuitsthe validation, and nothing is cached across resources or over time, so this is a
per-instance value rather than a canonical path cache. The existing 7-arg
FileResourceconstructor is retained as a delegate.Please run at least
TestMapper*,TestContextConfig*, the webresources tests,and
TestDefaultServlet*.Performance
Measured on a build from source (JDK 25), median of 8 start/stop cycles, same
webapp set both sides. The gains scale with application size:
~560 ms → ~483 ms (~14%). Measured without the newest CanonicalPath cache, which even improves it more.
The canonical path reuse removes one syscall per file resource lookup. On a
resource-heavy deploy (JFR, Windows, ~500 classes under
WEB-INF/classes) iteliminated
FileResource.getCanonicalPath()from the profile entirely, 21 of 112canonicalize0samples. The effect is smaller on Linux, where path resolution isconsiderably cheaper than on Windows.
Changelog entries are included.
Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com